package models

import (
	"encoding/json"
	"strconv"
	"time"

	"github.com/astaxie/beego/cache"
	"github.com/beego/redigo/redis"
)

var Rc *RedisCache

var Keys = map[string][]string{}
var Rates = map[string]map[string]int{}

var Local = false

// actually do the redis cmds

// Redis cache adapter.
type RedisCache struct {
	p        *redis.Pool // redis connection pool
	conninfo string
}

// actually do the redis cmds
func (rc *RedisCache) Do(commandName string, args ...interface{}) (reply interface{}, err error) {
	c := rc.p.Get()
	defer c.Close()

	return c.Do(commandName, args...)
}

// Get cache from redis.
func (rc *RedisCache) LGet(key string) interface{} {
	v, err := rc.Do("LRANGE", key, 0, 0)
	if err != nil {
		return nil
	}
	if v != nil && len(v.([]interface{})) > 0 {
		return v.([]interface{})[0]
	}
	return nil
}

// Get cache from redis.
func (rc *RedisCache) LGetAll(key string) []string {
	v, err := rc.Do("LRANGE", key, 0, 9999999)
	if err != nil {
		return nil
	}
	if v != nil && len(v.([]interface{})) > 0 {
		array := make([]string, 0)
		for _, value := range v.([]interface{}) {
			array = append(array, cache.GetString(value))
		}
		return array
	}
	return nil
}

// put cache to redis.
// timeout is ignored.
func (rc *RedisCache) LPut(key string, val interface{}) error {
	_, err := rc.Do("LPUSH", key, val)
	return err
}

// put cache to redis.
// timeout is ignored.
func (rc *RedisCache) LRem(key string, val interface{}) error {
	_, err := rc.Do("LREM", key, 0, val)
	return err
}

// Get cache from redis.
func (rc *RedisCache) HGet(key string, hkey string) interface{} {
	v, err := rc.Do("HGET", key, hkey)
	if err != nil {
		return nil
	}
	return v
}

// Get cache from redis.
func (rc *RedisCache) HSet(key string, hkey string, val interface{}) error {
	_, err := rc.Do("HSET", key, hkey, val)
	if err != nil {
		println("HSet,", err.Error())
	}
	return err
}

// Get cache from redis.
func (rc *RedisCache) HGetAll(key string) map[string]string {
	v, err := rc.Do("HGETALL", key)
	if err != nil {
		return nil
	}
	data := map[string]string{}
	k := ""
	for _, d := range v.([]interface{}) {
		//fmt.Printf("%v\n", cache.GetString(d))
		if k == "" {
			k = cache.GetString(d)
		} else {
			data[k] = cache.GetString(d)
			k = ""
		}
	}
	return data
}

// delete cache in redis.
func (rc *RedisCache) Delete(key string) error {
	_, err := rc.Do("DEL", key)
	return err
}

// check cache exist in redis.
func (rc *RedisCache) IsExist(key string) bool {
	v, err := redis.Bool(rc.Do("EXISTS", key))
	if err != nil {
		return false
	}
	return v
}

// clean all cache in redis. delete this redis collection.
func (rc *RedisCache) ClearAll() error {
	_, err := rc.Do("FLUSHDB")
	return err
}

func init() {
	Rc = &RedisCache{}
	Rc.conninfo = "127.0.0.1:6379"
	// initialize a new pool
	Rc.p = &redis.Pool{
		MaxIdle:     3,
		IdleTimeout: 180 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", Rc.conninfo)
			if err != nil {
				return nil, err
			}
			return c, nil
		},
	}
	//Rc.Do("select", 0)
}

func Gc() {
	keys, err := Rc.Do("KEYS", "*")
	if err == nil {
		for _, key := range keys.([]interface{}) {
			value := map[string]string{}
			json.Unmarshal([]byte(cache.GetString(Rc.LGet(cache.GetString(key)))), &value)
			if len(value) > 0 {
				if v, ok := value["t"]; ok {
					t, _ := strconv.Atoi(v)
					now := time.Now().Unix()
					if now-int64(t) >= 120 {
						Rc.Delete(cache.GetString(key))
					}
				}
				Rc.Do("LTRIM", cache.GetString(key), 0, 99)
			}
		}
	} else {
		println("error to get keys!")
	}
}
